/*
 * @ (#) CmppPacket.java Jun 8, 2008
 *
 * 
 */
package com.aspire.nm.component.cmppserver.filter.coder.packet;

import java.nio.ByteBuffer;



/**
 * CMPP协议封包
 *
 * @author	Wang Shenggong
 * @version 1.0.0
 * @since 1.0.0
 *
 */
public abstract class CmppPacket extends Packet implements java.io.Serializable{
	
	private static final long serialVersionUID = 1L;



	public Object bizParams;
	
	
	
	public static final int CMPP_CONNECT = 0x00000001;
	public static final int CMPP_CONNECT_LENGTH = 39;
	public static final int CMPP_CONNECT_RESP = 0x80000001;
	public static final int CMPP_CONNECT_RESP_LENGTH_V30 = 33;
	public static final int CMPP_CONNECT_RESP_LENGTH_V20 = 30;
	public static final int CMPP_TERMINATE = 0x00000002;
	public static final int CMPP_TERMINATE_LENGTH = 12;
	public static final int CMPP_TERMINATE_RESP = 0x80000002;
	public static final int CMPP_TERMINATE_RESP_LENGTH = 12;
	public static final int CMPP_SUBMIT = 0x00000004;
	public static final int CMPP_SUBMIT_LENGTH_MIN_V30 = 163;
	public static final int CMPP_SUBMIT_LENGTH_MIN_V20 = 136;
	public static final int CMPP_SUBMIT_RESP = 0x80000004;
	public static final int CMPP_SUBMIT_RESP_LENGTH_V30 = 24;
	public static final int CMPP_SUBMIT_RESP_LENGTH_V20 = 21;
	public static final int CMPP_DELIVER = 0x00000005;
	public static final int CMPP_DELIVER_LENGTH_MIN_V30 = 109;
	public static final int CMPP_DELIVER_LENGTH_MIN_V20 = 85;
	public static final int CMPP_DELIVER_RESP = 0x80000005;
	public static final int CMPP_DELIVER_RESP_LENGTH_V30 = 24;
	public static final int CMPP_DELIVER_RESP_LENGTH_V20 = 21;
	public static final int CMPP_QUERY = 0x00000006;
	public static final int CMPP_QUERY_LENGTH_V30 = -1;
	public static final int CMPP_QUERY_RESP = 0x80000006;
	public static final int CMPP_QUERY_RESP_LENGTH_V30 = -1;
	public static final int CMPP_CANCEL = 0x00000007;
	public static final int CMPP_CANCEL_LENGTH_V30 = -1;
	public static final int CMPP_CANCEL_RESP = 0x80000007;
	public static final int CMPP_CANCEL_RESP_LENGTH_V30 = -1;
	public static final int CMPP_ACTIVE_TEST = 0x00000008;
	public static final int CMPP_ACTIVE_TEST_LENGTH = 12;
	public static final int CMPP_ACTIVE_TEST_RESP = 0x80000008;
	public static final int CMPP_ACTIVE_TEST_RESP_LENGTH = 13;
	
	public static final int USMP_SUBMITPUSH = 0x10000001;
	public static final int USMP_SUBMITPUSH_LENGTH_MIN = 161;
	public static final int USMP_SUBMITPUSH_LENGTH_MAX = 3761;
	public static final int USMP_SUBMITPUSH_RESP = 0x90000001;
	public static final int USMP_SUBMITPUSH_RESP_LENGTH = 24;
	public static final int USMP_PUSHNOTIFY = 0x10000002;
	public static final int USMP_PUSHNOTIRY_LENGTH = 68;
	public static final int USMP_PUSHNOTIRY_RESP = 0x90000002;
	public static final int USMP_PUSHNOTIRY_RESP_LENGTH = 24;
	
	public static final int HEADER_LENGTH = 12;
	
	/**
	 * 解释封包所使用的CMPP版本
	 */
	protected Version version = Version.UNKNOW_VERSION;
	
	/**
	 * CMPP指令标识
	 */
	protected int commandId;
	
	/**
	 * CMPP消息序号
	 */
	protected int sequenceId;
	
	/**
	 * CMPP封包长度
	 */
	protected int totalLength;
	
	/**
	 * 构造CMPP封包
	 */
	public CmppPacket(){
		// nothing...
	}
	
	/**
	 * 用CMPP版本号初始化Cmpp封包
	 * @param version
	 */
	public CmppPacket(Version version){
		this.version = version;
	}
	
	/**
	 * 未知指令
	 */
	public static CmppPacket UNKNOW_PACKET = new CmppPacket(){
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public byte[] pack() throws PacketException{
			
			return null;
		}

		public CmppPacket unpack(ByteBuffer buffer)  throws PacketException{
			super.unpack(buffer);
			
			return this;
		}
		
	};
	
	
	/**
	 * 申请消息空间, 并构造CMPP消息头
	 * @param totalLength
	 * @param commandId
	 * @param sequenceId
	 */
	private void packHeader(int totalLength, int commandId, int sequenceId)  throws PacketException{
		
		try {
			
			allocate(totalLength);
			
			putInt(totalLength);
			putInt(commandId);
			putInt(sequenceId);
			
		} catch (Exception e) {
			throw new PacketException("Unexpected Exception on packing CmppPacket header: TotalLength=" + totalLength + ", CommandId=" + Integer.toHexString(commandId) + ", SequenceId=" + sequenceId, e);
		}
		
	}
	
	/**
	 * 对CMPP消息头解包
	 * @throws PacketException
	 */
	private void unpackHeader() throws PacketException{
		
		try{
			totalLength = getInt();
		}catch(Exception e){
			throw new PacketException("Unexpected Exception on packing CmppPacket header: ", e);
		}
		
		if(totalLength < 12)
			throw new PacketException("CmppPacket header invalid: TotalLength=" + totalLength + ", CommandId=0x" + Integer.toHexString(commandId) + ", SequenceId=" + sequenceId);
		
		commandId = getInt();
		sequenceId = getInt();
		
		if(!this.isValidHeader())
			throw new PacketException("CmppPacket header invalid: TotalLength=" + totalLength + ", CommandId=0x" + Integer.toHexString(commandId) + ", SequenceId=" + sequenceId);
		
	}
	
	/**
	 * 判断消息头是否为合法的CMPP消息头
	 * 
	 * @param commandId
	 * @return
	 */
	protected boolean isValidHeader(){
		boolean r = false;
		
		switch(commandId){
			case CMPP_CONNECT : 
				r = totalLength == CMPP_CONNECT_LENGTH ? true : false; 
				break;
			case CMPP_CONNECT_RESP : 
				r = totalLength == (version.isVersion3() ? CMPP_CONNECT_RESP_LENGTH_V30 : CMPP_CONNECT_RESP_LENGTH_V20) ? true : false; 
				break;
			case CMPP_TERMINATE : 
				r = totalLength == CMPP_TERMINATE_LENGTH ? true : false; 
				break;
			case CMPP_TERMINATE_RESP : 
				r = totalLength == CMPP_TERMINATE_RESP_LENGTH ? true : false; 
				break;
			case CMPP_SUBMIT : 
				r = totalLength > (version.isVersion3() ? CMPP_SUBMIT_LENGTH_MIN_V30 : CMPP_SUBMIT_LENGTH_MIN_V20) ? true : false; 
				break;
			case CMPP_SUBMIT_RESP : 
				r = totalLength == (version.isVersion3() ? CMPP_SUBMIT_RESP_LENGTH_V30 : CMPP_SUBMIT_RESP_LENGTH_V20) ? true : false; 
				break;
			case CMPP_DELIVER : 
				r = totalLength > (version.isVersion3() ? CMPP_DELIVER_LENGTH_MIN_V30 : CMPP_DELIVER_LENGTH_MIN_V20) ? true : false; 
				break;
			case CMPP_DELIVER_RESP : 
				r = totalLength == (version.isVersion3() ? CMPP_DELIVER_RESP_LENGTH_V30 : CMPP_DELIVER_RESP_LENGTH_V20) ? true : false; 
				break;
			case CMPP_QUERY : 
				r = totalLength == CMPP_QUERY_LENGTH_V30 ? true : false; 
				break;
			case CMPP_QUERY_RESP : 
				r = totalLength == CMPP_QUERY_RESP_LENGTH_V30 ? true : false; 
				break;
			case CMPP_CANCEL : 
				r = totalLength == CMPP_CANCEL_LENGTH_V30 ? true : false; 
				break;
			case CMPP_CANCEL_RESP : 
				r = totalLength == CMPP_CANCEL_RESP_LENGTH_V30 ? true : false; 
				break;
			case CMPP_ACTIVE_TEST : 
				r = totalLength == CMPP_ACTIVE_TEST_LENGTH ? true : false; 
				break;
			case CMPP_ACTIVE_TEST_RESP : 
				r = totalLength == CMPP_ACTIVE_TEST_RESP_LENGTH ? true : false; 
				break;
			case USMP_SUBMITPUSH :
				r = totalLength >= USMP_SUBMITPUSH_LENGTH_MIN && totalLength <= USMP_SUBMITPUSH_LENGTH_MAX ? true : false;
				break;
			case USMP_PUSHNOTIRY_RESP :
				r = totalLength == USMP_PUSHNOTIRY_RESP_LENGTH ? true : false;
				break;
		}
		
		return r;
	}
	
	/**
	 * 封包, 将封包对象打包成网络传输的字节数组
	 * @return 打包后的字节数组
	 */
	public byte[] pack() throws PacketException {
		packHeader(totalLength, commandId, sequenceId);
		return this.getBytes();
	}
	
	/**
	 * 将底层字节数组解包成封包对象, 解包后对象本身相应的值发生改变被将自己返回
	 * @param bytes 字节数组
	 * @return 解包后的CmppPacket对象
	 */
	public CmppPacket unpack(ByteBuffer buffer) throws PacketException{
		this.setByteBuffer(buffer);
		
		unpackHeader();
		
		return this;
	}
	
	/**
	 * @return the commandId
	 */
	public int getCommandId() {
		return commandId;
	}
	/**
	 * @param commandId the commandId to set
	 */
	public void setCommandId(int commandId) {
		this.commandId = commandId;
	}
	/**
	 * @return the sequenceId
	 */
	public int getSequenceId() {
		return sequenceId;
	}
	/**
	 * @param sequenceId the sequenceId to set
	 */
	public void setSequenceId(int sequenceId) {
		this.sequenceId = sequenceId;
	}
	/**
	 * @return the totalLength
	 */
	public int getTotalLength() {
		return totalLength;
	}
	/**
	 * @param totalLength the totalLength to set
	 */
	public void setTotalLength(int totalLength) {
		this.totalLength = totalLength;
	}
	
	/**
	 * 
	 * @param version
	 */
	public void setVersion(Version version){
		this.version = version;
	}
}
